﻿using System;
using System.Linq;
using Tycho.Compiler;
using Tycho.Parser;
using Tycho.Parser.Peg;
using Tycho.Runtime;
using Tycho.Parser.Tokens;

namespace Tycho.Grammar {
    public class TermTranslator : ITermTranslator {
        private readonly Func<SourceInformation, SourceLocation> ToSourceLocation;

        public TermTranslator () : this (new SourceInformationTranslator ().ToSourceLocation) {}

        public TermTranslator (Func<SourceInformation, SourceLocation> toSourceLocation) {
            ToSourceLocation = toSourceLocation;
        }

        public AnyObject Translate (ITerm term) {
            SourceLocation sloc = ToSourceLocation (term.SourceInformation);

            if (term is StringTerm) {
                return Terms.FromString (((StringTerm) term).Value, sloc);
            } else if (term is IntegerTerm) {
                return Terms.FromInteger (((IntegerTerm) term).Value, sloc);
            } else if (term is FloatingPointTerm) {
                return Terms.FromReal (((FloatingPointTerm) term).Value, sloc);
            } else if (term is IdentifierTerm) {
                return Terms.FromIdentifier (((IdentifierTerm) term).Name, null, sloc);
            } else if (term is CompositeTerm) {
                return TranslateCompositeTerm (term, sloc);
            } else if (term is ListTerm) {
                return TranslateListTerm (term, sloc);
            } else {
                throw new Exception ("term type not recognised for translation: " + term);
            }
        }

        private AnyObject TranslateListTerm (ITerm term, SourceLocation location) {
            var listTerm = (ListTerm) term;

            AnyObject obj = CompilerModule.CreateList (listTerm.Terms.Select (t => Translate (t)).ToArray ());

            return obj;
        }

        private AnyObject TranslateCompositeTerm (ITerm term, SourceLocation sloc) {
            var compositeTerm = (CompositeTerm) term;
            AnyObject obj = CompilerModule.CreateTerm (sloc,
                                                       Symbols.ParserTermName,
                                                       Namespaces.Parser.Get (compositeTerm.Name));

            foreach (var subTerm in compositeTerm.SubTerms) {
                obj.SetProperty (Namespaces.Parser.Get (subTerm.Key), Translate (subTerm.Value));
            }

            return obj;
        }
    }
}
